<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  

  
  <title>Abel&#39;Blog</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta property="og:type" content="website">
<meta property="og:title" content="Abel&#39;Blog">
<meta property="og:url" content="http://example.com/page/3/index.html">
<meta property="og:site_name" content="Abel&#39;Blog">
<meta property="og:locale" content="en_US">
<meta property="article:author" content="Abel Sean">
<meta name="twitter:card" content="summary">
  
    <link rel="alternate" href="/atom.xml" title="Abel&#39;Blog" type="application/atom+xml">
  
  
    <link rel="icon" href="/favicon.png">
  
  
    <link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
  
  
<link rel="stylesheet" href="/css/style.css">

<meta name="generator" content="Hexo 5.4.0"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">Abel&#39;Blog</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/">Home</a>
        
          <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="Search"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="http://example.com"></form>
      </div>
    </div>
  </div>
</header>
      <div class="outer">
        <section id="main">
  
    <article id="post-2020/2020-11-25-go-debug" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/11/25/2020/2020-11-25-go-debug/" class="article-date">
  <time datetime="2020-11-24T16:00:00.000Z" itemprop="datePublished">2020-11-25</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/11/25/2020/2020-11-25-go-debug/">go-debug</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>在Linux里面使用gdb/delve工具来调试程序。</p>
<h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><h2 id="Linux"><a href="#Linux" class="headerlink" title="Linux"></a>Linux</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">go get github.com/go-delve/delve/cmd/dlv</span><br></pre></td></tr></table></figure>

<p>vscode中的-workspace中配置一份调试选项：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;configurations&quot;</span>: [</span><br><span class="line">	&#123;</span><br><span class="line">		<span class="attr">&quot;name&quot;</span>: <span class="string">&quot;Attach to Process&quot;</span>,</span><br><span class="line">		<span class="attr">&quot;type&quot;</span>: <span class="string">&quot;go&quot;</span>,</span><br><span class="line">		<span class="attr">&quot;request&quot;</span>: <span class="string">&quot;attach&quot;</span>,</span><br><span class="line">		<span class="attr">&quot;mode&quot;</span>: <span class="string">&quot;local&quot;</span>,</span><br><span class="line">		<span class="comment">// &quot;remotePath&quot;: &quot;$&#123;workspaceFolder:bin&#125;\\Lobby.exe&quot;,</span></span><br><span class="line">		<span class="attr">&quot;presentation&quot;</span>: &#123;</span><br><span class="line">			<span class="attr">&quot;hidden&quot;</span>: <span class="literal">false</span>,</span><br><span class="line">			<span class="attr">&quot;group&quot;</span>: <span class="string">&quot;1&quot;</span>,</span><br><span class="line">			<span class="attr">&quot;order&quot;</span>: <span class="number">2</span></span><br><span class="line">		&#125;,</span><br><span class="line">		<span class="attr">&quot;processId&quot;</span>: <span class="number">2956</span></span><br><span class="line">	&#125;,</span><br></pre></td></tr></table></figure>

<p>使用vscode，安装连接到远程，工具栏里面选择“运行”-“安装附加调试器”</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Could not attach to pid 2956: this could be caused by a kernel security setting, try writing &quot;0&quot; to /proc/sys/kernel/yama/ptrace_scope</span><br></pre></td></tr></table></figure>

<h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul>
<li>[1] <a target="_blank" rel="noopener" href="https://github.com/derekparker/delve">github-delv</a></li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2020/11/25/2020/2020-11-25-go-debug/" data-id="ckrn3an79001520xwgk247d2u" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/golang/" rel="tag">golang</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-2020/2020-11-22-Recast" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/11/22/2020/2020-11-22-Recast/" class="article-date">
  <time datetime="2020-11-21T16:00:00.000Z" itemprop="datePublished">2020-11-22</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/11/22/2020/2020-11-22-Recast/">Recast-入门</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h1 id="Recast-入门"><a href="#Recast-入门" class="headerlink" title="Recast-入门"></a>Recast-入门</h1><p>Recast-Navigation 在做3d游戏的时候，用于做导航的在Unity3D、Unreal Engine都可以使用。当前使用 unreal 4.25</p>
<h2 id="1-Recast工程介绍"><a href="#1-Recast工程介绍" class="headerlink" title="1. Recast工程介绍"></a>1. Recast工程介绍</h2><p>RecastNavigation 是一个的导航寻路工具集，它包括了几个子集：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Recast：负责根据提供的模型生成导航网格。</span><br><span class="line">Detour：利用导航网格做寻路操作。这里的导航网格可以是 Recast 生成的，也可以是其他工具生成的。</span><br><span class="line">DetourCrowd：提供了群体寻路行为的功能。</span><br><span class="line">Recast Demo：一个很完善的 Demo，基本上将 Recast 、 Detour 提供的功能都很好地展现了出来。弄懂了这个 Demo 的功能，基本也就了解了 RecastNavigation 究竟可以干什么事。</span><br></pre></td></tr></table></figure>

<p>Recast先从几何体构造素模，然后在其上投射导航网格。此过程包括三个步骤：</p>
<ol>
<li>创建素模(voxel mold)；</li>
</ol>
<p>通过将三角形栅格化为多层高度场，从输入三角形网格构建体素模具。然后将一些简单的滤镜应用到模具上，以修剪角色无法移动的位置。</p>
<ol start="2">
<li>将素模划分成简单区域(region)；</li>
</ol>
<p>模具描述的可行走区域分为简单的2D覆盖区域。 生成的区域仅具有不重叠的轮廓，这大大简化了过程的最后一步。</p>
<ol start="3">
<li>将区域转换成多边形(polygons)；</li>
</ol>
<p>通过首先跟踪边界，然后对其进行简化，可以将导航多边形从区域中剥离。 最终将生成的多边形转换为凸多边形，这使它们非常适合该级的寻路和空间推理。</p>
<blockquote>
<p>注：凸多边形的内角均小于或等于180°，边数为n（n属于Z且n大于2）的凸多边形内角和为（n－2）×180°，但任意凸多边形外角和均为360°，并可通过反证法证明凸多边形内角中锐角的个数不能多于3个。</p>
</blockquote>
<p>尝试将unreal engine 里面的地图导出成detour能使用的导航网格：</p>
<p>再 UNREAL ENGINE 里面地图是*.umap文件。我们用UE打开之后，在菜单栏里面操作： Windows-&gt;导出NavMesh。这个步骤将会生成一个*.obj文件。这种文件其实是一种文本文件，描述了3D对象模型信息。Unity3D也一样能导出这样的文件。此过程将会生成某个尺寸的模型文件。</p>
<p>我们移植了一些Detour的代码，编写了一个程序 RecastCreator。这个程序能将*.obj文件导出成为一个binary文件。后续就直接使用binary文件来做导航操作。</p>
<figure class="highlight bat"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line">PS D:\work\trunk\refer\recastnavigation\RecastDemo\Bin\Meshes&gt; .\RecastCreator.exe -cfg -out t3.obj t3.region</span><br><span class="line">----- Create Cfg -----</span><br><span class="line">cellsize = <span class="number">0</span>.<span class="number">30</span></span><br><span class="line">cellheight = <span class="number">0</span>.<span class="number">20</span></span><br><span class="line">agentheight = <span class="number">2</span></span><br><span class="line">agentradius = <span class="number">0</span>.<span class="number">6</span></span><br><span class="line">agentmaxclimb = <span class="number">0</span>.<span class="number">9</span></span><br><span class="line">agentmaxslope = <span class="number">45</span></span><br><span class="line">regionminsize = <span class="number">8</span></span><br><span class="line">regionmergesize = <span class="number">20</span></span><br><span class="line">edgemaxlen = <span class="number">12</span></span><br><span class="line">edgemaxerror = <span class="number">1</span>.<span class="number">3</span></span><br><span class="line">vertsperpoly = <span class="number">6</span></span><br><span class="line">detailsampledist = <span class="number">6</span></span><br><span class="line">detailsamplemaxerror = <span class="number">1</span></span><br><span class="line">partitiontype = <span class="number">1</span></span><br><span class="line">tilesize = <span class="number">0</span></span><br><span class="line">----- End -----</span><br><span class="line">PS D:\work\trunk\refer\recastnavigation\RecastDemo\Bin\Meshes&gt; .\RecastCreator.exe -out t3.obj t3.region</span><br><span class="line">----- Create Bin -----</span><br><span class="line">Building navigation:</span><br><span class="line"> - <span class="number">1028</span> x <span class="number">1135</span> cells</span><br><span class="line"> - <span class="number">525</span>.<span class="number">2</span>K verts, <span class="number">306</span>.<span class="number">2</span>K tris</span><br><span class="line">Build Times</span><br><span class="line">- Rasterize:    <span class="number">176</span>.<span class="number">99</span>ms        (<span class="number">24</span>.<span class="number">5</span>%)</span><br><span class="line">- Build <span class="built_in">Compact</span>:        <span class="number">39</span>.<span class="number">54</span>ms (<span class="number">5</span>.<span class="number">5</span>%)</span><br><span class="line">- Filter Border:        <span class="number">29</span>.<span class="number">55</span>ms (<span class="number">4</span>.<span class="number">1</span>%)</span><br><span class="line">- Filter Walkable:      <span class="number">5</span>.<span class="number">15</span>ms  (<span class="number">0</span>.<span class="number">7</span>%)</span><br><span class="line">- Erode Area:   <span class="number">19</span>.<span class="number">18</span>ms (<span class="number">2</span>.<span class="number">7</span>%)</span><br><span class="line">- Median Area:  <span class="number">0</span>.<span class="number">00</span>ms  (<span class="number">0</span>.<span class="number">0</span>%)</span><br><span class="line">- Mark Box Area:        <span class="number">0</span>.<span class="number">00</span>ms  (<span class="number">0</span>.<span class="number">0</span>%)</span><br><span class="line">- Mark Convex Area:     <span class="number">0</span>.<span class="number">00</span>ms  (<span class="number">0</span>.<span class="number">0</span>%)</span><br><span class="line">- Mark Cylinder Area:   <span class="number">0</span>.<span class="number">00</span>ms  (<span class="number">0</span>.<span class="number">0</span>%)</span><br><span class="line">- Build Distance Field: <span class="number">29</span>.<span class="number">01</span>ms (<span class="number">4</span>.<span class="number">0</span>%)</span><br><span class="line">    - Distance: <span class="number">19</span>.<span class="number">88</span>ms (<span class="number">2</span>.<span class="number">7</span>%)</span><br><span class="line">    - Blur:     <span class="number">8</span>.<span class="number">95</span>ms  (<span class="number">1</span>.<span class="number">2</span>%)</span><br><span class="line">- Build Regions:        <span class="number">105</span>.<span class="number">62</span>ms        (<span class="number">14</span>.<span class="number">6</span>%)</span><br><span class="line">    - Watershed:        <span class="number">97</span>.<span class="number">48</span>ms (<span class="number">13</span>.<span class="number">5</span>%)</span><br><span class="line">      - Expand: <span class="number">35</span>.<span class="number">47</span>ms (<span class="number">4</span>.<span class="number">9</span>%)</span><br><span class="line">      - <span class="built_in">Find</span> Basins:    <span class="number">2</span>.<span class="number">93</span>ms  (<span class="number">0</span>.<span class="number">4</span>%)</span><br><span class="line">    - Filter:   <span class="number">6</span>.<span class="number">76</span>ms  (<span class="number">0</span>.<span class="number">9</span>%)</span><br><span class="line">- Build Layers: <span class="number">0</span>.<span class="number">00</span>ms  (<span class="number">0</span>.<span class="number">0</span>%)</span><br><span class="line">- Build Contours:       <span class="number">13</span>.<span class="number">14</span>ms (<span class="number">1</span>.<span class="number">8</span>%)</span><br><span class="line">    - Trace:    <span class="number">8</span>.<span class="number">87</span>ms  (<span class="number">1</span>.<span class="number">2</span>%)</span><br><span class="line">    - Simplify: <span class="number">2</span>.<span class="number">00</span>ms  (<span class="number">0</span>.<span class="number">3</span>%)</span><br><span class="line">- Build Polymesh:       <span class="number">6</span>.<span class="number">09</span>ms  (<span class="number">0</span>.<span class="number">8</span>%)</span><br><span class="line">- Build Polymesh Detail:        <span class="number">286</span>.<span class="number">67</span>ms        (<span class="number">39</span>.<span class="number">6</span>%)</span><br><span class="line">- Merge Polymeshes:     <span class="number">0</span>.<span class="number">00</span>ms  (<span class="number">0</span>.<span class="number">0</span>%)</span><br><span class="line">- Merge Polymesh Details:       <span class="number">0</span>.<span class="number">00</span>ms  (<span class="number">0</span>.<span class="number">0</span>%)</span><br><span class="line">=== TOTAL:      <span class="number">723</span>.<span class="number">73</span>ms</span><br><span class="line">&gt;&gt; Polymesh: <span class="number">2552</span> vertices  <span class="number">1153</span> polygons</span><br><span class="line"></span><br><span class="line">----- End -----</span><br></pre></td></tr></table></figure>

<h2 id="2-环境搭建"><a href="#2-环境搭建" class="headerlink" title="2.环境搭建"></a>2.环境搭建</h2><h3 id="2-1-编译recastnavigation"><a href="#2-1-编译recastnavigation" class="headerlink" title="2.1.编译recastnavigation"></a>2.1.编译recastnavigation</h3><table>
<thead>
<tr>
<th align="left">工具名称</th>
<th align="left">版本信息</th>
</tr>
</thead>
<tbody><tr>
<td align="left">visualstudio2019</td>
<td align="left">16.8.3</td>
</tr>
<tr>
<td align="left">CMake</td>
<td align="left">3.19.0-rc1</td>
</tr>
<tr>
<td align="left">premake</td>
<td align="left">5.0</td>
</tr>
</tbody></table>
<h4 id="2-1-1-下载地址"><a href="#2-1-1-下载地址" class="headerlink" title="2.1.1.下载地址"></a>2.1.1.下载地址</h4><table>
<thead>
<tr>
<th align="left">名称</th>
<th align="left">下载地址</th>
</tr>
</thead>
<tbody><tr>
<td align="left">SDL</td>
<td align="left"><a target="_blank" rel="noopener" href="https://www.libsdl.org/release/SDL2-devel-2.0.14-VC.zip">SDL2-devel-2.0.14-VC.zip</a></td>
</tr>
<tr>
<td align="left">premake</td>
<td align="left"><a target="_blank" rel="noopener" href="https://premake.github.io/">premake.github</a></td>
</tr>
</tbody></table>
<h4 id="2-1-2-准备好SDL2的dll"><a href="#2-1-2-准备好SDL2的dll" class="headerlink" title="2.1.2.准备好SDL2的dll"></a>2.1.2.准备好SDL2的dll</h4><p>将下载的SDL2.dll文件放入到子目录中：</p>
<figure class="highlight bat"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">recastnavigation\RecastDemo\Contrib\SDL\lib\x86\SDL2.dll</span><br></pre></td></tr></table></figure>

<p>使用CMake将SDL2编译出来。在windows里面，我这里都是使用<code>32位</code>编译的。</p>
<p>配置下列的环境变量：</p>
<table>
<thead>
<tr>
<th align="left">变量名</th>
<th align="left">位置</th>
</tr>
</thead>
<tbody><tr>
<td align="left">SDL_PATH</td>
<td align="left">D:\work\trunk\refer\recastnavigation\RecastDemo\Contrib\SDL</td>
</tr>
<tr>
<td align="left">SDL2_INCLUDE_DIR</td>
<td align="left">D:\work\trunk\refer\SDL\SDL2-2.0.14\SDL2-2.0.14\include</td>
</tr>
<tr>
<td align="left">SDL2_LIBRARY</td>
<td align="left">D:\work\trunk\refer\SDL\SDL2-2.0.14\SDL2-2.0.14\build\Debug</td>
</tr>
<tr>
<td align="left">SDL2_SDLMAIN_LIBRARY</td>
<td align="left">D:\work\trunk\refer\SDL\SDL2-2.0.14\SDL2-2.0.14\build\Debug</td>
</tr>
</tbody></table>
<h4 id="2-1-3-使用premake5生成工程文件"><a href="#2-1-3-使用premake5生成工程文件" class="headerlink" title="2.1.3.使用premake5生成工程文件"></a>2.1.3.使用premake5生成工程文件</h4><p>这个默认生成的是win32位的</p>
<figure class="highlight bat"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">D:\<span class="title">work</span>\<span class="title">trunk</span>\<span class="title">refer</span>\<span class="title">recastnavigation</span>\<span class="title">RecastDemo</span>&gt;&quot;<span class="title">premake5</span>&quot; <span class="title">vs2019</span></span></span><br><span class="line"><span class="function"><span class="title">Building</span> <span class="title">configurations</span>...</span></span><br><span class="line"><span class="function"><span class="title">Running</span> <span class="title">action</span> &#x27;<span class="title">vs2019</span>&#x27;...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">recastnavigation.sln</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">DebugUtils.vcxproj</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">DebugUtils.vcxproj.filters</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">Detour.vcxproj</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">Detour.vcxproj.filters</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">DetourCrowd.vcxproj</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">DetourCrowd.vcxproj.filters</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">DetourTileCache.vcxproj</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">DetourTileCache.vcxproj.filters</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">Recast.vcxproj</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">Recast.vcxproj.filters</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">RecastDemo.vcxproj</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">RecastDemo.vcxproj.user</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">RecastDemo.vcxproj.filters</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">Tests.vcxproj</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">Tests.vcxproj.user</span>...</span></span><br><span class="line"><span class="function"><span class="title">Generated</span> <span class="title">Build</span>/<span class="title">vs2019</span>/<span class="title">Tests.vcxproj.filters</span>...</span></span><br><span class="line"><span class="function"><span class="title">Done</span> (225<span class="title">ms</span>).</span></span><br></pre></td></tr></table></figure>

<p>工程属性-&gt;生成事件-&gt;命令行；将下面部分去掉</p>
<figure class="highlight bat"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>&gt;找不到文件 - SDL2.dll</span><br><span class="line"><span class="number">1</span>&gt;C:\Program Files (x86)\Microsoft Visual Studio\<span class="number">2019</span>\Enterprise\MSBuild\Microsoft\VC\v160\Microsoft.CppCommon.targets(<span class="number">153</span>,<span class="number">5</span>): error MSB3073: 命令“<span class="keyword">IF</span> <span class="keyword">EXIST</span> &quot;..\..\Contrib\SDL\lib\x86\SDL2.dll&quot;\ (<span class="built_in">xcopy</span> /Q /E /Y /I &quot;..\..\Contrib\SDL\lib\x86\SDL2.dll&quot; &quot;..\..\Bin&quot; &gt; <span class="built_in">nul</span>) <span class="keyword">ELSE</span> (<span class="built_in">xcopy</span> /Q /Y /I &quot;..\..\Contrib\SDL\lib\x86\SDL2.dll&quot; &quot;..\..\Bin&quot; &gt; <span class="built_in">nul</span>)</span><br><span class="line"><span class="number">1</span>&gt;C:\Program Files (x86)\Microsoft Visual Studio\<span class="number">2019</span>\Enterprise\MSBuild\Microsoft\VC\v160\Microsoft.CppCommon.targets(<span class="number">153</span>,<span class="number">5</span>): error MSB3073: :VCEnd”已退出，代码为 <span class="number">4</span>。</span><br><span class="line"><span class="number">1</span>&gt;已完成生成项目“RecastDemo.vcxproj”的操作 - 失败。</span><br><span class="line"></span><br><span class="line"><span class="keyword">IF</span> <span class="keyword">EXIST</span> &quot;..\..\Contrib\SDL\lib\x86\SDL2.dll&quot;\ (<span class="built_in">xcopy</span> /Q /E /Y /I &quot;..\..\Contrib\SDL\lib\x86\SDL2.dll&quot; &quot;..\..\Bin&quot; &gt; <span class="built_in">nul</span>) <span class="keyword">ELSE</span> (<span class="built_in">xcopy</span> /Q /Y /I &quot;..\..\Contrib\SDL\lib\x86\SDL2.dll&quot; &quot;..\..\Bin&quot; &gt; <span class="built_in">nul</span>)</span><br></pre></td></tr></table></figure>

<p>自己手动将编译出来的文件复制过去。</p>
<p>RecastDemo的效果图：</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/rr16JS"><img src="https://s3.ax1x.com/2020/12/22/rr16JS.png" alt="rr16JS.png"></a></p>
<p>加载一个obj文件：</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/rrHrJs"><img src="https://s3.ax1x.com/2020/12/22/rrHrJs.png" alt="rrHrJs.png"></a></p>
<p>将obj文件的导航网格生成出来：</p>
<p>生成了之后，地图的颜色将会变化，只需要点击”Build”。有导航网格，才能去测试导航功能。</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/rrHsWn"><img src="https://s3.ax1x.com/2020/12/22/rrHsWn.png" alt="rrHsWn.png"></a></p>
<p>测试导航功能：</p>
<p>在地图上选择开始结束点；”shift+鼠标左键”</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/rrHcQ0"><img src="https://s3.ax1x.com/2020/12/22/rrHcQ0.png" alt="rrHcQ0.png"></a></p>
<h2 id="3-Recast-导航网格类型"><a href="#3-Recast-导航网格类型" class="headerlink" title="3.Recast 导航网格类型"></a>3.Recast 导航网格类型</h2><p>recastnavigation中提供了3种模式的导航网格：</p>
<h3 id="3-1-SoloMesh"><a href="#3-1-SoloMesh" class="headerlink" title="3.1.SoloMesh"></a>3.1.SoloMesh</h3><p>其中SoloMesh模式是静态的导航网格，即对场景build一次之后，将导航网格缓存起来供寻路使用，后续不再允许场景的导航信息发生变化。</p>
<p>文件头：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">int</span> NAVMESHSET_MAGIC = <span class="string">&#x27;M&#x27;</span>&lt;&lt;<span class="number">24</span> | <span class="string">&#x27;S&#x27;</span>&lt;&lt;<span class="number">16</span> | <span class="string">&#x27;E&#x27;</span>&lt;&lt;<span class="number">8</span> | <span class="string">&#x27;T&#x27;</span>; <span class="comment">//&#x27;MSET&#x27;;</span></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">NavMeshSetHeader</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="keyword">uint32_t</span> magic;</span><br><span class="line">    <span class="keyword">uint32_t</span> version;</span><br><span class="line">    <span class="keyword">uint32_t</span> numTiles;</span><br><span class="line">    dtNavMeshParams params;</span><br><span class="line">&#125;;</span><br><span class="line">    <span class="comment">// Store header.</span></span><br><span class="line">    NavMeshSetHeader header;</span><br><span class="line">    header.magic = NAVMESHSET_MAGIC;</span><br><span class="line">    header.version = NAVMESHSET_VERSION;</span><br><span class="line">    header.numTiles = <span class="number">0</span>;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="3-2-TileMesh"><a href="#3-2-TileMesh" class="headerlink" title="3.2.TileMesh"></a>3.2.TileMesh</h3><p>TileMesh也是静态的导航网格，只是与SoloMesh相比它按tile来处理地图，算是介于SoloMesh和TempObstacles之间的一种模式</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 文件名：all_tiles_navmesh.bin</span></span><br><span class="line"><span class="comment">// 文件头： TESM</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">int</span> NAVMESHSET_MAGIC = <span class="string">&#x27;M&#x27;</span>&lt;&lt;<span class="number">24</span> | <span class="string">&#x27;S&#x27;</span>&lt;&lt;<span class="number">16</span> | <span class="string">&#x27;E&#x27;</span>&lt;&lt;<span class="number">8</span> | <span class="string">&#x27;T&#x27;</span>; <span class="comment">//&#x27;MSET&#x27;;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 加载文件失败：</span></span><br><span class="line">dtStatus addTileStatus = m_tileCache-&gt;<span class="built_in">addTile</span>(data, tileHeader.dataSize, DT_COMPRESSEDTILE_FREE_DATA, &amp;tile);</span><br><span class="line"><span class="function"><span class="keyword">inline</span> <span class="keyword">bool</span> <span class="title">dtStatusFailed</span><span class="params">(dtStatus status)</span></span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">int</span> DT_FAILURE </span>= <span class="number">1u</span> &lt;&lt; <span class="number">31</span>;            <span class="comment">// Operation failed.</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">unsigned</span> <span class="keyword">int</span> DT_WRONG_MAGIC = <span class="number">1</span> &lt;&lt; <span class="number">0</span>;        <span class="comment">// Input data is not recognized.</span></span><br><span class="line">    <span class="keyword">if</span> (header-&gt;magic != DT_TILECACHE_MAGIC)</span><br><span class="line">        <span class="keyword">return</span> DT_FAILURE | DT_WRONG_MAGIC;</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">int</span> DT_TILECACHE_MAGIC = <span class="string">&#x27;D&#x27;</span>&lt;&lt;<span class="number">24</span> | <span class="string">&#x27;T&#x27;</span>&lt;&lt;<span class="number">16</span> | <span class="string">&#x27;L&#x27;</span>&lt;&lt;<span class="number">8</span> | <span class="string">&#x27;R&#x27;</span>; <span class="comment">///&lt; &#x27;DTLR&#x27;;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">Sample::<span class="built_in">saveAll</span>(<span class="string">&quot;all_tiles_navmesh.bin&quot;</span>, m_navMesh);</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="3-3-TempObstacles"><a href="#3-3-TempObstacles" class="headerlink" title="3.3.TempObstacles"></a>3.3.TempObstacles</h3><p>TempObstacles模式可以支持向场景中动态添加或移除预设形状的阻挡物，导航网格也会随之更新（不过只支持添加阻挡物，而不支持添加新的可行走区域）在处理动态阻挡时，由于单个阻挡对地图的影响区域是有限的，所以会采用将地图切割成多个固定大小的tile，以tile为单位进行网格的生成。这样在添加或移除阻挡时，只需要处理与阻挡相交的tile，而不需要处理整个地图。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">const</span> <span class="keyword">int</span> TILECACHESET_MAGIC = <span class="string">&#x27;T&#x27;</span> &lt;&lt; <span class="number">24</span> | <span class="string">&#x27;S&#x27;</span> &lt;&lt; <span class="number">16</span> | <span class="string">&#x27;E&#x27;</span> &lt;&lt; <span class="number">8</span> | <span class="string">&#x27;T&#x27;</span>; <span class="comment">//&#x27;TSET&#x27;;</span></span><br></pre></td></tr></table></figure>

<p><a target="_blank" rel="noopener" href="https://github.com/ZLSYMDJ/NavMeshDemo">参考NavMeshDemo源码</a></p>
<h3 id="3-4-导出Mesh的源码分析"><a href="#3-4-导出Mesh的源码分析" class="headerlink" title="3.4.导出Mesh的源码分析"></a>3.4.导出Mesh的源码分析</h3><p>参考官方的代码-RecastDemo部分，在右边的上部，”Sample”中下拉菜单中选择的三类(Solo Mesh、Tile Mesh、Temp Obstacles)导出方式都看明白。</p>
<p>3种导出方式：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Sample* <span class="title">createSolo</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Sample_SoloMesh</span>(); &#125;</span><br><span class="line"><span class="function">Sample* <span class="title">createTile</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Sample_TileMesh</span>(); &#125;</span><br><span class="line"><span class="function">Sample* <span class="title">createTempObstacle</span><span class="params">()</span> </span>&#123; <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Sample_TempObstacles</span>(); &#125;</span><br></pre></td></tr></table></figure>

<p>“Build”按钮将会调用</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="built_in">imguiButton</span>(<span class="string">&quot;Build&quot;</span>))</span><br><span class="line">&#123;</span><br><span class="line">    ctx.<span class="built_in">resetLog</span>();</span><br><span class="line">    <span class="keyword">if</span> (!sample-&gt;<span class="built_in">handleBuild</span>())</span><br><span class="line">    &#123;</span><br><span class="line">        showLog = <span class="literal">true</span>;</span><br><span class="line">        logScroll = <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    ctx.<span class="built_in">dumpLog</span>(<span class="string">&quot;Build log %s:&quot;</span>, meshName.<span class="built_in">c_str</span>());</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Clear test.</span></span><br><span class="line">    <span class="keyword">delete</span> test;</span><br><span class="line">    test = <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>“Save”按钮将会把生成的网格存储到</p>
<table>
<thead>
<tr>
<th align="left">类型</th>
<th align="left">文件</th>
<th align="left">大小</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Solo</td>
<td align="left">solo_navmesh.bin</td>
<td align="left">240kb</td>
</tr>
<tr>
<td align="left">Tile</td>
<td align="left">all_tiles_navmesh.bin</td>
<td align="left">479kb</td>
</tr>
<tr>
<td align="left">TempObstacle</td>
<td align="left">all_tiles_tilecache.bin</td>
<td align="left">479kb</td>
</tr>
</tbody></table>
<p>导出时候需要设置的参数：</p>
<table>
<thead>
<tr>
<th align="left">变量</th>
<th align="left">基本信息</th>
<th align="left">备注</th>
</tr>
</thead>
<tbody><tr>
<td align="left">cellsize</td>
<td align="left">方块尺寸</td>
<td align="left">0.3</td>
</tr>
<tr>
<td align="left">cellheight</td>
<td align="left">方块高度</td>
<td align="left">0.2</td>
</tr>
<tr>
<td align="left">agentheight</td>
<td align="left">walkableHeight = agentheight/ cellheight</td>
<td align="left">2</td>
</tr>
<tr>
<td align="left">agentradius</td>
<td align="left">walkableClimb = agentradius/ cellheight</td>
<td align="left">0.6</td>
</tr>
<tr>
<td align="left">agentmaxclimb</td>
<td align="left">walkableRadius = agentmaxclimb/ cellheight</td>
<td align="left">0.9</td>
</tr>
<tr>
<td align="left">agentmaxslope</td>
<td align="left">walkableSlopeAngle = agentmaxslope</td>
<td align="left">45</td>
</tr>
<tr>
<td align="left">regionminsize</td>
<td align="left">minRegionArea = regionminsize*regionminsize</td>
<td align="left">8</td>
</tr>
<tr>
<td align="left">regionmergesize</td>
<td align="left">mergeRegionArea = regionmergesize*regionmergesize</td>
<td align="left">20</td>
</tr>
<tr>
<td align="left">edgemaxlen</td>
<td align="left">maxEdgeLen = edgemaxlen/cellheight</td>
<td align="left">12</td>
</tr>
<tr>
<td align="left">edgemaxerror</td>
<td align="left">maxSimplificationError = edgemaxerror</td>
<td align="left">1.3</td>
</tr>
<tr>
<td align="left">vertsperpoly</td>
<td align="left">maxVertsPerPoly</td>
<td align="left">6</td>
</tr>
<tr>
<td align="left">detailsampledist</td>
<td align="left">detailSampleDist = detailsampledist &lt; 0.9f ? 0 : cellsize * detailsampledist;</td>
<td align="left">6</td>
</tr>
<tr>
<td align="left">detailsamplemaxerror</td>
<td align="left">detailSampleMaxError = cellheight * detailsamplemaxerror</td>
<td align="left">1</td>
</tr>
<tr>
<td align="left">partitiontype</td>
<td align="left"></td>
<td align="left">1</td>
</tr>
<tr>
<td align="left">tilesize</td>
<td align="left"></td>
<td align="left">48</td>
</tr>
</tbody></table>
<p><a target="_blank" rel="noopener" href="http://www.stevefsp.org/projects/rcndoc/prod/structrcConfig.html">RecastNavigation-rcConfig文档</a></p>
<p>cellsize</p>
<p>方块尺寸（x,z平面上），和平面有关系。较低的值将产生较高质量的导航，但是图形扫描较慢。在文档里面记作 [vx]</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/ymUU0I"><img src="https://s3.ax1x.com/2021/02/02/ymUU0I.gif" alt="ymUU0I.gif"></a></p>
<p>cellheight</p>
<p>方块高度（y高程），和爬坡有关系。记录计算攀爬仰角，攀爬的高度，计算边缘都需要这个。官方文档里面记作 [vy]</p>
<p>agentheight</p>
<hr>
<p>minRegionArea</p>
<p>最小孤岛所容纳的cell数目；</p>
<p>任意区域小于指定的数目，将会标记成无法行走。这个东西通常用于去删除无用几何体，比如桌子的顶部，盒子的顶部等等。</p>
<p>值域：[Limit: &gt;=0] 作用域：[Units: vx]，vx代表平面；</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/ymUNnA"><img src="https://s3.ax1x.com/2021/02/02/ymUNnA.gif" alt="ymUNnA.gif"></a></p>
<p>mergeRegionArea</p>
<p>如果有可能性的话，任意区域如果cell的数目小于它，将会被融合到比它大的区域里面。</p>
<p>值域：[Limit: &gt;=0] 作用域：[Units: vx]</p>
<p>edgemaxlen</p>
<p>沿网格边界的轮廓边缘的最大允许长度。 [Limit: &gt;=0] [Units: vx].</p>
<p>这个将会让边缘更加趋于直线。如果想关闭这个功能将值设置成0。</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/ymUz9O"><img src="https://s3.ax1x.com/2021/02/02/ymUz9O.gif" alt="ymUz9O.gif"></a></p>
<p>partitiontype</p>
<p>分区方式</p>
<p>Watershed 分水岭方式</p>
<p>enum SamplePartitionType SAMPLE_PARTITION_WATERSHED = 0</p>
<p>Monotone 单调方式</p>
<p>enum SamplePartitionType SAMPLE_PARTITION_MONOTONE = 1</p>
<p>Layers 分层方式</p>
<p>enum SamplePartitionType SAMPLE_PARTITION_LAYERS = 2</p>
<p>原文释义：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">Partition the heightfield so that we can use simple algorithm later to triangulate the walkable areas.</span><br><span class="line">There are 3 martitioning methods, each with some pros and cons:</span><br><span class="line">1) Watershed partitioning</span><br><span class="line">  - the classic Recast partitioning</span><br><span class="line">  - creates the nicest tessellation</span><br><span class="line">  - usually slowest</span><br><span class="line">  - partitions the heightfield into nice regions without holes or overlaps</span><br><span class="line">  - the are some corner cases where this method creates produces holes and overlaps</span><br><span class="line">     - holes may appear when a small obstacles is close to large open area (triangulation can handle this)</span><br><span class="line">     - overlaps may occur if you have narrow spiral corridors (i.e stairs), this make triangulation to fail</span><br><span class="line">  * generally the best choice if you precompute the nacmesh, use this if you have large open areas</span><br><span class="line">2) Monotone partioning</span><br><span class="line">  - fastest</span><br><span class="line">  - partitions the heightfield into regions without holes and overlaps (guaranteed)</span><br><span class="line">  - creates long thin polygons, which sometimes causes paths with detours</span><br><span class="line">  * use this if you want fast navmesh generation</span><br><span class="line">3) Layer partitoining</span><br><span class="line">  - quite fast</span><br><span class="line">  - partitions the heighfield into non-overlapping regions</span><br><span class="line">  - relies on the triangulation code to cope with holes (thus slower than monotone partitioning)</span><br><span class="line">  - produces better triangles than monotone partitioning</span><br><span class="line">  - does not have the corner cases of watershed partitioning</span><br><span class="line">  - can be slow and create a bit ugly tessellation (still better than monotone)</span><br><span class="line">    if you have large open areas with small obstacles (not a problem if you use tiles)</span><br><span class="line">  * good choice to use for tiled navmesh with medium and small sized tiles</span><br></pre></td></tr></table></figure>

<p>tilesize</p>
<p>每块tile再xz平面上的size。[Limit: &gt;= 0] [Units: vx].</p>
<p>这个值旨在编译出多tile mesh的时候生效（temp obstacles/tiles）。</p>
<h2 id="4-包围盒分类"><a href="#4-包围盒分类" class="headerlink" title="4.包围盒分类"></a>4.包围盒分类</h2><h3 id="4-1-AABB"><a href="#4-1-AABB" class="headerlink" title="4.1.AABB"></a>4.1.AABB</h3><p>AABB算法的全称是 - axis aligned bounding box (轴对齐-边界盒)AABB 包围盒是与坐标轴对齐的包围盒, 简单性好, 紧密性较差。</p>
<h3 id="4-2-OBB"><a href="#4-2-OBB" class="headerlink" title="4.2.OBB"></a>4.2.OBB</h3><p>OBB 包围盒: OBB（oriented bounding box 方向矩形边界框） 碰撞检測方法紧密性是较好的, 可以大大降低參与相交測试的包围盒的数目, 因此整体性能要优于AABB 和包围球, 而且实时性程度较高。</p>
<h2 id="阅读-Export-Recast-Navigation-Data-from-UE4"><a href="#阅读-Export-Recast-Navigation-Data-from-UE4" class="headerlink" title="阅读 Export Recast Navigation Data from UE4"></a>阅读 Export Recast Navigation Data from UE4</h2><p>Library: <code>ue-recast-detour</code>这个是从 UE 代码中抽取出的 <code>recast detour</code> 库，在 UE 源码的路径下为 Runtime/Navmesh/Detour，主要目的是保证 UE 客户端与外部服务器的验证方法一致。只将solo方式的导航。</p>
<p>其实在<code>UnrealEngine4</code>的源代码中是有一份<code>NavigationSystem</code>，<code>Navmesh</code>。<a target="_blank" rel="noopener" href="https://github.com/EpicGames/UnrealEngine/tree/release/Engine/Source/Runtime/Navmesh">github-UE4-Navmesh</a>。可以直接在ue4的引擎代码里面找到动态阻挡相关的逻辑。</p>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul>
<li>[1] <a target="_blank" rel="noopener" href="https://github.com/recastnavigation/recastnavigation">recastnavigation</a></li>
<li>[2] <a target="_blank" rel="noopener" href="https://premake.github.io/download.html#v5">premake</a></li>
<li>[3] <a target="_blank" rel="noopener" href="https://www.libsdl.org/download-2.0.php">SDL</a></li>
<li>[4] <a target="_blank" rel="noopener" href="http://masagroup.github.io/recastdetour/index.html">recastdetour手册</a></li>
<li>[5] <a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/35100455">游戏的寻路导航 1：导航网格</a></li>
<li>[6] <a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_34132768/article/details/93762363">recastNavigation读书笔记1</a></li>
<li>[7] <a target="_blank" rel="noopener" href="http://critterai.org/projects/nmgen_study/overview.html">CritterAI</a></li>
<li>[8] <a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV1uK4y1E7CV?from=search&seid=8637375892047322871">B站讲解RecastDemo如何使用</a></li>
<li>[9] <a target="_blank" rel="noopener" href="https://www.jianshu.com/p/64469a410b5d">从零开始学习导航网格</a></li>
<li>[10] <a target="_blank" rel="noopener" href="https://www.cnblogs.com/mfrbuaa/p/4073880.html">AABB&amp;OBB包围盒</a></li>
<li>[11] <a target="_blank" rel="noopener" href="https://blog.csdn.net/Kathy_unity/article/details/81583968">生成寻路网格的配置</a></li>
<li>[12] <a target="_blank" rel="noopener" href="https://arongranberg.com/astar/docs/createnavmesh.html">手动创建navmesh</a></li>
<li>[13] <a target="_blank" rel="noopener" href="https://arongranberg.com/astar/docs/recastgraph.html">navmesh参数</a></li>
<li>[14] <a target="_blank" rel="noopener" href="https://imzlp.com/posts/20203/">ue-navmesh</a></li>
<li>[15] <a target="_blank" rel="noopener" href="https://imzlp.com/posts/20203/">循迹研究室-Export Recast Navigation</a></li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2020/11/22/2020/2020-11-22-Recast/" data-id="ckrn3an7q002f20xwbuab8hi0" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Game/" rel="tag">Game</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/recast/" rel="tag">recast</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-2020/2020-11-21-docker-custom-devenv" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/11/21/2020/2020-11-21-docker-custom-devenv/" class="article-date">
  <time datetime="2020-11-20T16:00:00.000Z" itemprop="datePublished">2020-11-21</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/11/21/2020/2020-11-21-docker-custom-devenv/">docker自定义环境</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>学习docker，自定义运行环境；收集一些信息来做参考；慢慢收集信息。</p>
<ul>
<li><a href="#%E5%A4%B4%E9%83%A8">头部</a></li>
<li><a href="#%E8%BF%90%E8%A1%8C%E7%A8%8B%E5%BA%8F">运行程序</a></li>
<li><a href="#expose">EXPOSE</a></li>
<li><a href="#env">ENV</a></li>
<li><a href="#user">USER</a></li>
<li><a href="#arg">ARG</a></li>
<li><a href="#%E5%8F%82%E8%80%83">参考</a></li>
</ul>
<p>Dockerfile 其实就是构建image的text文本。</p>
<h1 id="头部"><a href="#头部" class="headerlink" title="头部"></a>头部</h1><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 定制的镜像都是基于 FROM 的镜像</span></span><br><span class="line"><span class="keyword">FROM</span> centos:<span class="number">7.7</span>.<span class="number">1908</span></span><br><span class="line"><span class="keyword">MAINTAINER</span> xxx@xxx.com</span><br><span class="line"><span class="comment"># 定制文件目录</span></span><br><span class="line"><span class="keyword">WORKDIR</span><span class="bash"> /root</span></span><br></pre></td></tr></table></figure>

<h1 id="运行程序"><a href="#运行程序" class="headerlink" title="运行程序"></a>运行程序</h1><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 依赖包和sshd配置</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \</span></span><br><span class="line"><span class="bash">    &amp;&amp; mkdir -p .ssh php /app/data/site /app/<span class="built_in">local</span>/nginx/conf/vhosts \</span></span><br><span class="line"><span class="bash">    &amp;&amp; yum install -y procps-ng iproute net-tools less wget bzip2 \</span></span><br><span class="line"><span class="bash">    openssh-clients openssh-server \</span></span><br><span class="line"><span class="bash">    gcc gcc-c++ autoconf automake libtool make cmake \</span></span><br><span class="line"><span class="bash">    zlib-devel openssl-devel pcre-devel \</span></span><br><span class="line"><span class="bash">    libaio curl-devel libxml2-devel glibc-devel glib2-devel gd-devel gd2-devel libjpeg-devel libpng-devel freetype-devel \</span></span><br><span class="line"><span class="bash">    &amp;&amp; ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key \</span></span><br><span class="line"><span class="bash">    &amp;&amp; ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> tar zxf openssl-1.1.1c.tar.gz \</span></span><br><span class="line"><span class="bash">    &amp;&amp; tar zxf nginx-1.14.2.tar.gz \</span></span><br><span class="line"><span class="bash">    &amp;&amp; <span class="built_in">cd</span> nginx-1.14.2 \</span></span><br><span class="line"><span class="bash">    &amp;&amp; ./configure --user=www --group=www \</span></span><br><span class="line"><span class="bash">    --prefix=/app/<span class="built_in">local</span>/nginx \</span></span><br><span class="line"><span class="bash">    --with-http_stub_status_module \</span></span><br><span class="line"><span class="bash">    --with-openssl=../openssl-1.1.1c \</span></span><br><span class="line"><span class="bash">    --with-openssl-opt=<span class="string">&#x27;enable-tls1_3 enable-weak-ssl-ciphers&#x27;</span> \</span></span><br><span class="line"><span class="bash">    --with-http_ssl_module \</span></span><br><span class="line"><span class="bash">    --with-http_realip_module \</span></span><br><span class="line"><span class="bash">    --with-http_sub_module \</span></span><br><span class="line"><span class="bash">    --with-pcre \</span></span><br><span class="line"><span class="bash">    --with-http_v2_module \</span></span><br><span class="line"><span class="bash">    &amp;&amp; make -j2 &amp;&amp; make install \</span></span><br><span class="line"><span class="bash">    &amp;&amp; <span class="built_in">cd</span> &amp;&amp; rpm -ivh filebeat-7.5.1-x86_64.rpm \</span></span><br><span class="line"><span class="bash">    &amp;&amp; rm -rf nginx-* openssl-1.1.1c* filebeat* \</span></span><br><span class="line"><span class="bash">    &amp;&amp; useradd www</span></span><br><span class="line"><span class="comment"># 复制权限文件</span></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> authorized_keys /root/.ssh/authorized_keys</span></span><br></pre></td></tr></table></figure>

<h1 id="EXPOSE"><a href="#EXPOSE" class="headerlink" title="EXPOSE"></a>EXPOSE</h1><p>仅仅只是声明端口</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">EXPOSE</span> <span class="number">22</span> <span class="number">80</span></span><br></pre></td></tr></table></figure>

<h1 id="ENV"><a href="#ENV" class="headerlink" title="ENV"></a>ENV</h1><p>设置环境变量，定义了环境变量，那么在后续的指令中，就可以使用这个环境变量。</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ENV</span> &lt;key&gt; &lt;value&gt;</span><br><span class="line"><span class="keyword">ENV</span> &lt;key1&gt;=&lt;value1&gt; &lt;key2&gt;=&lt;value2&gt;...</span><br></pre></td></tr></table></figure>
<h1 id="USER"><a href="#USER" class="headerlink" title="USER"></a>USER</h1><p>用于指定执行后续命令的用户和用户组，这边只是切换后续命令执行的用户（用户和用户组必须提前已经存在）。</p>
<p>格式：</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">USER</span> &lt;用户名&gt;[:&lt;用户组&gt;]</span><br></pre></td></tr></table></figure>

<h1 id="ARG"><a href="#ARG" class="headerlink" title="ARG"></a>ARG</h1><p>构建参数，与 ENV 作用一至。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效，也就是说只有 docker build 的过程中有效，构建好的镜像内不存在此环境变量。</p>
<p>构建命令 docker build 中可以用 –build-arg &lt;参数名&gt;=&lt;值&gt; 来覆盖。</p>
<p>格式：</p>
<figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ARG</span> &lt;参数名&gt;[=&lt;默认值&gt;]</span><br></pre></td></tr></table></figure>

<h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul>
<li>[1] <a target="_blank" rel="noopener" href="https://www.runoob.com/docker/docker-dockerfile.html">菜鸟Dockerfile说明</a></li>
<li>[2] <a target="_blank" rel="noopener" href="https://docs.docker.com/engine/reference/builder/">官网参考</a></li>
<li>[3] <a target="_blank" rel="noopener" href="https://www.cnblogs.com/dazhoushuoceshi/p/7066041.html">Dockerfile命令详解（超全版本）</a></li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2020/11/21/2020/2020-11-21-docker-custom-devenv/" data-id="ckrn3an77001120xw41zf4wbm" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E8%BF%90%E7%BB%B4/" rel="tag">运维</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-2020/2020-11-21-cannon-guitarsheet" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/11/21/2020/2020-11-21-cannon-guitarsheet/" class="article-date">
  <time datetime="2020-11-20T16:00:00.000Z" itemprop="datePublished">2020-11-21</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/11/21/2020/2020-11-21-cannon-guitarsheet/">卡农吉他谱</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>卡农吉他谱：</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/D8dF8H"><img src="https://s3.ax1x.com/2020/11/22/D8dF8H.jpg" alt="D8dF8H.jpg"></a></p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/D8diPe"><img src="https://s3.ax1x.com/2020/11/22/D8diPe.jpg" alt="D8diPe.jpg"></a></p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/D8dC5D"><img src="https://s3.ax1x.com/2020/11/22/D8dC5D.jpg" alt="D8dC5D.jpg"></a></p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/D8d9UO"><img src="https://s3.ax1x.com/2020/11/22/D8d9UO.jpg" alt="D8d9UO.jpg"></a></p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/D8dAxA"><img src="https://s3.ax1x.com/2020/11/22/D8dAxA.jpg" alt="D8dAxA.jpg"></a></p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/D8dk2d"><img src="https://s3.ax1x.com/2020/11/22/D8dk2d.jpg" alt="D8dk2d.jpg"></a></p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2020/11/21/2020/2020-11-21-cannon-guitarsheet/" data-id="ckrn3an78001320xw2zcl9o5u" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/guitar/" rel="tag">guitar</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-2020/2020-11-18-FSM-Golang" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/11/18/2020/2020-11-18-FSM-Golang/" class="article-date">
  <time datetime="2020-11-17T16:00:00.000Z" itemprop="datePublished">2020-11-18</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/11/18/2020/2020-11-18-FSM-Golang/">FSM-golang</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>使用FSM（Finite State Machine）来制作怪物的AI，比较轻量级；</p>
<h1 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h1><p>状态机分为两大类：</p>
<table>
<thead>
<tr>
<th align="left">名称</th>
<th align="left">说明</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Moore machine</td>
<td align="left">输出只和状态有关而与输入无关</td>
</tr>
<tr>
<td align="left">Mealy machine</td>
<td align="left">输出不仅和状态有关而且和输入有关系（使用这种状态机通常可以减少状态的数量。）</td>
</tr>
</tbody></table>
<p>状态机可归纳为4个要素，即现态、条件、动作、次态。这样的归纳，主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因，“动作”和“次态”是果。详解如下：</p>
<p>①现态：是指当前所处的状态。</p>
<p>②条件：又称为“事件”，当一个条件被满足，将会触发一个动作，或者执行一次状态的迁移。</p>
<p>③动作：条件满足后执行的动作。动作执行完毕后，可以迁移到新的状态，也可以仍旧保持原状态。动作不是必需的，当条件满足后，也可以不执行任何动作，直接迁移到新状态。</p>
<p>④次态：条件满足后要迁往的新状态。“次态”是相对于“现态”而言的，“次态”一旦被激活，就转变成新的“现态”了。</p>
<p>最首先状态机的概念是使用在电路，单片机相关的东西里面。</p>
<blockquote>
<p>在刚才归纳的要素里面很重要的就是“状态”，“条件“，”动作“。</p>
</blockquote>
<h1 id="游戏中常用的状态机"><a href="#游戏中常用的状态机" class="headerlink" title="游戏中常用的状态机"></a>游戏中常用的状态机</h1><p><a target="_blank" rel="noopener" href="https://imgchr.com/i/DulcAU"><img src="https://s3.ax1x.com/2020/11/19/DulcAU.png" alt="DulcAU.png"></a></p>
<table>
<thead>
<tr>
<th align="left">要素</th>
<th align="left">元素</th>
</tr>
</thead>
<tbody><tr>
<td align="left">状态</td>
<td align="left">待机状态</br>巡逻状态</br>追击状态</br>攻击状态</td>
</tr>
<tr>
<td align="left">条件</td>
<td align="left">出现敌人</br>主动怪判断</br>遭到攻击</br>攻击范围检测</br>追击范围检测</td>
</tr>
<tr>
<td align="left">动作</td>
<td align="left">朝某个位置移动</br>施法</td>
</tr>
</tbody></table>
<h1 id="阅读-looplab-fsm-实现"><a href="#阅读-looplab-fsm-实现" class="headerlink" title="阅读 looplab/fsm 实现"></a>阅读 looplab/fsm 实现</h1><p><a target="_blank" rel="noopener" href="https://imgchr.com/i/DKZZLD"><img src="https://s3.ax1x.com/2020/11/19/DKZZLD.png" alt="DKZZLD.png"></a></p>
<p>在创建状态机的时候需要指定状态之间切换的可能性；需要指定在状态切换的时候的调用。（状态是直接使用string来表示）</p>
<p>核心对象FMS，里面缓存了有限状态机的路由，并且能接受外部提供的Event的触发；</p>
<p>当Event区触发当前的有限状态机的时候，将会做状态的切换；</p>
<p>切换状态的时候，需要使用到 transitioner 对象；</p>
<p>参考实例代码：</p>
<figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line">    <span class="string">&quot;fmt&quot;</span></span><br><span class="line">    <span class="string">&quot;github.com/looplab/fsm&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Door 门对象</span></span><br><span class="line"><span class="keyword">type</span> Door <span class="keyword">struct</span> &#123;</span><br><span class="line">    To  <span class="keyword">string</span></span><br><span class="line">    <span class="comment">// FSM 有限状态机对象</span></span><br><span class="line">    FSM *fsm.FSM</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// NewDoor 新建门对象</span></span><br><span class="line"><span class="comment">// to 名称</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">NewDoor</span><span class="params">(to <span class="keyword">string</span>)</span> *<span class="title">Door</span></span> &#123;</span><br><span class="line">    d := &amp;Door&#123;</span><br><span class="line">        To: to,</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    d.FSM = fsm.NewFSM(</span><br><span class="line">        <span class="comment">// 默认的状态名称</span></span><br><span class="line">        <span class="string">&quot;closed&quot;</span>,</span><br><span class="line">        fsm.Events&#123;</span><br><span class="line">            <span class="comment">// 事件 open ，能将状态机从 closed 转换成 open 状态</span></span><br><span class="line">            &#123;Name: <span class="string">&quot;open&quot;</span>, Src: []<span class="keyword">string</span>&#123;<span class="string">&quot;closed&quot;</span>&#125;, Dst: <span class="string">&quot;open&quot;</span>&#125;,</span><br><span class="line">            <span class="comment">// 事件 close ，能将状态机从 closed 转换成 open 状态</span></span><br><span class="line">            &#123;Name: <span class="string">&quot;close&quot;</span>, Src: []<span class="keyword">string</span>&#123;<span class="string">&quot;open&quot;</span>&#125;, Dst: <span class="string">&quot;closed&quot;</span>&#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">        fsm.Callbacks&#123;</span><br><span class="line">            <span class="comment">// 给状态机注册 enter_state 回调函数</span></span><br><span class="line">            <span class="string">&quot;enter_state&quot;</span>: <span class="function"><span class="keyword">func</span><span class="params">(e *fsm.Event)</span></span> &#123; d.enterState(e) &#125;,</span><br><span class="line">        &#125;,</span><br><span class="line">    )</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> d</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// enterState 当进入新状态的回调</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(d *Door)</span> <span class="title">enterState</span><span class="params">(e *fsm.Event)</span></span> &#123;</span><br><span class="line">    fmt.Printf(<span class="string">&quot;The door to %s is %s\n&quot;</span>, d.To, e.Dst)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    door := NewDoor(<span class="string">&quot;heaven&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 发起 open 事件</span></span><br><span class="line">    err := door.FSM.Event(<span class="string">&quot;open&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        fmt.Println(err)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 发起 close 事件</span></span><br><span class="line">    err = door.FSM.Event(<span class="string">&quot;close&quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line">        fmt.Println(err)</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>状态机本身只关心状态切换，他自己不关心这个事件生成；</p>
<h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul>
<li>[1] <a target="_blank" rel="noopener" href="http://wiki.cnki.com.cn/HotWord/88305.htm">状态机概念</a></li>
<li>[2] <a target="_blank" rel="noopener" href="http://www.elecfans.com/emb/danpianji/2009020923861.html">状态机思路在单片机程序设计中的应用</a></li>
<li>[3] <a target="_blank" rel="noopener" href="https://github.com/looplab/fsm">looplab/fsm有限状态机实现</a></li>
<li>[4] <a target="_blank" rel="noopener" href="https://github.com/smallnest/gofsm">smallnest/gofsm有限状态机实现</a></li>
<li>[4] <a target="_blank" rel="noopener" href="https://www.cnblogs.com/sevenyuan/p/5276320.html">人工智能开发-决策方法</a></li>
<li>[5] <a target="_blank" rel="noopener" href="https://www.cnblogs.com/puputu/articles/1701012.html">erlang版本状态机</a></li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2020/11/18/2020/2020-11-18-FSM-Golang/" data-id="ckrn3an75000t20xwfut06l4w" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Game/" rel="tag">Game</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/golang/" rel="tag">golang</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-2020/2020-11-18-go[3]-接口" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/11/18/2020/2020-11-18-go%5B3%5D-%E6%8E%A5%E5%8F%A3/" class="article-date">
  <time datetime="2020-11-17T16:00:00.000Z" itemprop="datePublished">2020-11-18</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/11/18/2020/2020-11-18-go%5B3%5D-%E6%8E%A5%E5%8F%A3/">go[3]-接口</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <ul>
<li><a href="#%E6%A6%82%E8%BF%B0">概述</a></li>
<li><a href="#%E5%8C%85%E5%90%ABnil%E6%8C%87%E9%92%88%E7%9A%84%E6%8E%A5%E5%8F%A3%E4%B8%8D%E6%98%AFnil%E6%8E%A5%E5%8F%A3">包含nil指针的接口不是nil接口</a></li>
<li><a href="#%E7%B1%BB%E5%9E%8B%E6%96%AD%E8%A8%80">类型断言</a></li>
<li><a href="#%E4%B8%80%E4%BA%9B%E5%BB%BA%E8%AE%AE">一些建议</a></li>
<li><a href="#%E4%BD%BF%E7%94%A8golang%E7%BB%84%E5%90%88%E6%97%B6%E5%B8%B8%E7%8A%AF%E7%9A%84%E9%94%99">使用golang组合时常犯的错</a></li>
<li><a href="#%E5%BC%95%E7%94%A8">引用</a></li>
</ul>
<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>接口是对类型行为的抽象和概括。接口抽象的方法可以让我们函数更加灵活和适应性。中间只定义了函数类型。接口就是约定。</p>
<p>接口本事存储数据内容为：</p>
<blockquote>
<p>type: xxx<br>value: xxx</p>
</blockquote>
<p>定义实例</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> interfaceName <span class="keyword">interface</span> &#123;</span><br><span class="line">     Write([]<span class="keyword">byte</span>)</span><br><span class="line">     Read([]<span class="keyword">byte</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="包含nil指针的接口不是nil接口"><a href="#包含nil指针的接口不是nil接口" class="headerlink" title="包含nil指针的接口不是nil接口"></a>包含nil指针的接口不是nil接口</h1><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">var</span> buf *bytes.Buffer</span><br><span class="line"> f(buf)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">f</span><span class="params">(out io.Writer)</span></span> &#123;</span><br><span class="line">  <span class="keyword">if</span> out != <span class="literal">nil</span> &#123;</span><br><span class="line">     out.Write([]<span class="keyword">byte</span>(<span class="string">&quot;done!\n&quot;</span>))</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>虽然已经对out做了空的判断，但是依然会崩溃。原因是这个调用f函数的时候，给了一个空的*bytes.Buffer指针。所以接口本身不是空。</p>
<h1 id="类型断言"><a href="#类型断言" class="headerlink" title="类型断言"></a>类型断言</h1><p>x为一个interface，T为一个定义的类型。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> w io.Writer</span><br><span class="line">w = os.Stdout</span><br><span class="line">f := w.(*os.File) <span class="comment">// success</span></span><br><span class="line">e := w.(*bytes.Buffer) <span class="comment">// panic</span></span><br></pre></td></tr></table></figure>

<p>如果转换失败了，将会直接造成panic。如果断言操作的对象是一个nil接口，无论被断言成什么类型都会失败。</p>
<p>使用两个返回值方式的，第二个参数将会返回是否转换成功。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">b,ok :=w.(*bytes.Buffer)</span><br></pre></td></tr></table></figure>

<p>我们可以拿着ok变量来做逻辑。</p>
<p>对于类型的switch case语句可以这样来编写</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">sqlQuote</span><span class="params">(x <span class="keyword">interface</span>&#123;&#125;)</span> <span class="title">string</span></span> &#123;</span><br><span class="line">    <span class="keyword">switch</span> x:=x.(<span class="keyword">type</span>) &#123;</span><br><span class="line">    <span class="keyword">case</span> <span class="literal">nil</span>:</span><br><span class="line">         <span class="keyword">return</span> <span class="string">&quot;NULL&quot;</span></span><br><span class="line">    <span class="keyword">case</span> <span class="keyword">int</span>,<span class="keyword">uint</span>:</span><br><span class="line">         <span class="keyword">return</span> fmt.Sprintf(<span class="string">&quot;%d&quot;</span>,x)</span><br><span class="line">   <span class="keyword">default</span>:</span><br><span class="line">        <span class="built_in">panic</span>(fmt.Sprintf(<span class="string">&quot;unexpected type: %T: %v&quot;</span>,x,x))</span><br></pre></td></tr></table></figure>

<h1 id="一些建议"><a href="#一些建议" class="headerlink" title="一些建议"></a>一些建议</h1><p>设计一个新的包时，先创建一个接口的集合开始，后面定义满足它们具体的类型。每个接口都实现一个。请不要这么做，这种接口是不必要去抽象。只有两个或者以上的具体类型必须以相同的方法进行处理时候才需要。ask only for what you need.（奥卡姆剃刀）</p>
<h1 id="使用golang组合时常犯的错"><a href="#使用golang组合时常犯的错" class="headerlink" title="使用golang组合时常犯的错"></a>使用golang组合时常犯的错</h1><p>类关系：</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/yRbZk9"><img src="https://s3.ax1x.com/2021/02/18/yRbZk9.png" alt="yRbZk9.png"></a></p>
<p>当有一个B类，转换成InterfaceA提供给外部。</p>
<p>外部直接调用FunA。</p>
<p>classA类里面实现的FunA方法调用了FunB，这个时候将会造成问题。</p>
<p>当函数调到FunA的时候，就进入了classA类中了，调用了classA.FunB函数，而不是调用classB.FunB函数。</p>
<p>有些人为了解决这个问题，直接将FunA参数表里面增加一个InterfaceA的原始指针。</p>
<p>这种情况下，最好是对外的函数不要互相调用</p>
<p><a target="_blank" rel="noopener" href="https://github.com/swordhell/learn_go/tree/master/learn_interface">测试源码</a></p>
<p>可以按照下面的方式来实现这个结构：</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/yRbeYR"><img src="https://s3.ax1x.com/2021/02/18/yRbeYR.png" alt="yRbeYR.png"></a></p>
<p>对外部提供的接口 IMain 。</p>
<p>内部实现时 ISub ， IMain 聚合了 ISub ，而且比 ISub 多了一些公开的函数。</p>
<p>Main类里面没有任何的成员变量，是对工作流程的一种调配，不实现任何具体的功能。它内部聚合的 ISub 的实现，都依赖于 CSpecA，CSpecB。</p>
<p>CBase类有成员变量，在这块实现具体的功能。在CSpecA中直接可以将CBase类已经实现的函数，做特例化修改。</p>
<p>这样将会让程序更加的干净。</p>
<h1 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h1><ul>
<li>[1] <a target="_blank" rel="noopener" href="https://www.cnblogs.com/itplay/p/10671715.html">uml绘图</a></li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2020/11/18/2020/2020-11-18-go[3]-%E6%8E%A5%E5%8F%A3/" data-id="ckrn3an75000v20xw4zyf71dr" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/golang/" rel="tag">golang</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-2020/2020-11-18-go[4]-gogrutines" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/11/18/2020/2020-11-18-go%5B4%5D-gogrutines/" class="article-date">
  <time datetime="2020-11-17T16:00:00.000Z" itemprop="datePublished">2020-11-18</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/11/18/2020/2020-11-18-go%5B4%5D-gogrutines/">go[4]-goroutine</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><p>go语言中的并发程序通过两种手段来实现。goroutine和channel。顺序通讯进程（communicating sequential processes）简称CSP.<br>并发（concurrency）：逻辑上具备同时处理多个任务的能力。<br>并行（parallelism）：物理上再统一时刻执行多个并发任务。</p>
<h3 id="goroutine"><a href="#goroutine" class="headerlink" title="goroutine"></a>goroutine</h3><p>简单将goroutine归纳为协程不合适。运行时会创建多个线程来执行并发任务，很像多线程和协程的综合体，能最大限度提升执行效率，发挥多核处理能力。<br>go语句，创建一个并发任务单元，放置再系统队列中，等待调度安排系统线程去获取执行权，当前流程不会阻塞，不会等待任务的启动。每个任务单元保存函数指针，参数，还会分配执行的栈内存，相对系统默认的MB级别的栈内存，goroutine栈只有2KB。<br>等待单独的退出事件可以直接使用channel来制作</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    exit := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">struct</span>&#123;&#125;)</span><br><span class="line">    <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">       time.Sleep(time.Second)</span><br><span class="line">       fmt.Println(<span class="string">&quot;goroutine done.&quot;</span>)</span><br><span class="line">       <span class="built_in">close</span>(exit)</span><br><span class="line">    &#125;()</span><br><span class="line">    fmt.Println(<span class="string">&quot;main...&quot;</span>)</span><br><span class="line">    &lt;- exit</span><br><span class="line">    fmt.Println(<span class="string">&quot;main exit.&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果需要等待多个指令可以使用<br>sync.WaitGroup来处理。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line">wg.Add(<span class="number">1</span>)</span><br><span class="line">wg.Done()</span><br><span class="line">wg.Wait()</span><br></pre></td></tr></table></figure>
<p>一些有用的函数<br>runtime.NumCPU<br>查询服务器的cpu个数<br>runtime.GOMAXPROCS<br>参数小于1，表示查询当前设置的值，否则就是设置参与并发的线程数。<br>runtime.Gosched<br>暂停，释放线程去执行其他的任务，当前线程被放回队列，等待下次调度是恢复。<br>runtime.Goexit<br>在goroutine里面执行这个，将会终端任务，无论身处在哪一层，Goexit都能立即终止整个调用堆栈，这个和return退出当前函数不一样。但是不要在main函数里面调用这个函数,否则会直接panic。</p>
<h3 id="channel"><a href="#channel" class="headerlink" title="channel"></a>channel</h3><p>通道是用于连接两个不同的线程。分为同步和异步的两种通道。<br>如果通道的长度为0，就是同步通道，就是需要等待处理方开始处理之后，才会执行后续操作。<br>如果通道长度&gt;0，则是异步通道，投递了数据之后就能直接走了。<br>这个例子就是演示同步方式的通道，使用for来获取管道中的内容。</p>
<figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">    done := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">struct</span> &#123;&#125;)</span><br><span class="line">    c := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">    <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">        <span class="keyword">defer</span> <span class="built_in">close</span>(done)</span><br><span class="line">        <span class="keyword">for</span> x:=<span class="keyword">range</span> c&#123;</span><br><span class="line">             fmt.Println(x)</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;()</span><br><span class="line">    c &lt;- <span class="number">1</span></span><br><span class="line">    c &lt;- <span class="number">2</span></span><br><span class="line">    c &lt;<span class="number">-3</span></span><br><span class="line">    <span class="built_in">close</span>(c)</span><br><span class="line">    &lt;-done</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>通道默认是双向的。如果特殊情况下需要将通道的单向端获取出来，赋值给某个函数，控制其行为，只能单向，那就可以参考下面的代码</p>
<figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line">      <span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line">      wg.Add(<span class="number">2</span>)</span><br><span class="line">      c := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="keyword">int</span>)</span><br><span class="line">      <span class="keyword">var</span> send <span class="keyword">chan</span> &lt;- <span class="keyword">int</span> = c</span><br><span class="line">      <span class="keyword">var</span> recv &lt;- <span class="keyword">chan</span> = c</span><br><span class="line">      <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">          <span class="keyword">defer</span> wg.Done()</span><br><span class="line">          <span class="keyword">for</span> x := <span class="keyword">range</span> recv &#123;</span><br><span class="line">              <span class="built_in">println</span>(x)</span><br><span class="line">          &#125;</span><br><span class="line">     &#125;()</span><br><span class="line">     <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">          <span class="keyword">defer</span> wg.Done()</span><br><span class="line">          <span class="keyword">for</span> i:=<span class="number">0</span>;i&lt;<span class="number">3</span>;i++ &#123;</span><br><span class="line">               send &lt;-I</span><br><span class="line">         &#125;</span><br><span class="line">   &#125;()</span><br><span class="line">   wg.Wait()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这样就能把通道的单向通道赋值出来，  &lt;-send或者 recv &lt;- 1，语句就无法执行。<br>如果一个goroutine中要同时处理多个通道，可以选用select语句。它会随机选择一个可用通道收发操作。找一段leaf的源码来参考</p>
<figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 构造一个有缓冲区的chan</span></span><br><span class="line">...</span><br><span class="line"><span class="keyword">var</span> ChanCall  <span class="keyword">chan</span> *CallInfo</span><br><span class="line">...</span><br><span class="line">ChanCall = <span class="built_in">make</span>(<span class="keyword">chan</span> *CallInfo, <span class="number">1000</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(s *Skeleton)</span> <span class="title">Run</span><span class="params">(closeSig <span class="keyword">chan</span> <span class="keyword">bool</span>)</span></span> &#123;</span><br><span class="line">	<span class="keyword">for</span> &#123;</span><br><span class="line">		<span class="keyword">select</span> &#123;</span><br><span class="line">		<span class="keyword">case</span> &lt;-closeSig:</span><br><span class="line">			s.commandServer.Close()</span><br><span class="line">			s.server.Close()</span><br><span class="line">			<span class="keyword">for</span> !s.g.Idle() || !s.client.Idle() &#123;</span><br><span class="line">				s.g.Close()</span><br><span class="line">				s.client.Close()</span><br><span class="line">			&#125;</span><br><span class="line">			<span class="keyword">return</span></span><br><span class="line">		<span class="keyword">case</span> ri := &lt;-s.client.ChanAsynRet:</span><br><span class="line">			s.client.Cb(ri)</span><br><span class="line">		<span class="keyword">case</span> ci := &lt;-s.server.ChanCall:</span><br><span class="line">			s.server.Exec(ci)</span><br><span class="line">		<span class="keyword">case</span> ci := &lt;-s.commandServer.ChanCall:</span><br><span class="line">			s.commandServer.Exec(ci)</span><br><span class="line">		<span class="keyword">case</span> cb := &lt;-s.g.ChanCb:</span><br><span class="line">			s.g.Cb(cb)</span><br><span class="line">		<span class="keyword">case</span> t := &lt;-s.dispatcher.ChanTimer:</span><br><span class="line">			t.Cb()</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2020/11/18/2020/2020-11-18-go[4]-gogrutines/" data-id="ckrn3an76000x20xw8ru3br18" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/golang/" rel="tag">golang</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-2020/2020-11-18-go[5]-测试" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/11/18/2020/2020-11-18-go%5B5%5D-%E6%B5%8B%E8%AF%95/" class="article-date">
  <time datetime="2020-11-17T16:00:00.000Z" itemprop="datePublished">2020-11-18</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/11/18/2020/2020-11-18-go%5B5%5D-%E6%B5%8B%E8%AF%95/">go[4]-测试</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>提高代码的质量，有两个路数：1.多测试；2.做代码review；如果能在代码放到外部去之前，做好单元测试，将会节省很多的时间（如果能把事情做好，减少很多处理bug的时间）。</p>
<h3 id="单元测试（unit-test）"><a href="#单元测试（unit-test）" class="headerlink" title="单元测试（unit test）"></a>单元测试（unit test）</h3><p>单元测试能自动化，只是手段，而非目的。避免形式主义。需要持之以恒。想要习惯去做测试还是需要将测试的手段非常熟练，而且易用才能解开。</p>
<p>编写单元测试是对即将实现的算法做复核预演。</p>
<p>测试代码需要在当前包以_test.go结束的文件。</p>
<p>测试函数以Test为名字前缀。</p>
<p>测试命令go test忽略_,.开头的测试文件。</p>
<p>正常编译的时候，会忽略掉测试文件。</p>
<p>main_test.go</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span>(</span><br><span class="line">   <span class="string">&quot;testing&quot;</span></span><br><span class="line">)</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">add</span><span class="params">(x,y <span class="keyword">int</span>)</span> <span class="title">int</span></span> &#123;</span><br><span class="line">   <span class="keyword">return</span> x+y</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">TestAdd</span><span class="params">(t *testing.T)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> add(<span class="number">1</span>,<span class="number">2</span>) != <span class="number">3</span> &#123;</span><br><span class="line">        t.FailNow()</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$go</span> <span class="built_in">test</span> -v</span><br><span class="line">[cq@localhost test_case]$ go <span class="built_in">test</span> -v</span><br><span class="line">=== RUN   TestAdd</span><br><span class="line">--- PASS: TestAdd (0.00s)</span><br><span class="line">PASS</span><br><span class="line">ok  	test_case	0.002s</span><br></pre></td></tr></table></figure>

<p>遍历子包测试用例。</p>
<p>testing.T提供的函数</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>说明</th>
<th>相关</th>
</tr>
</thead>
<tbody><tr>
<td>Fail</td>
<td>失败：继续执行当前测试用例</td>
<td></td>
</tr>
<tr>
<td>FailNow</td>
<td>失败：终止当前测试</td>
<td>Failed</td>
</tr>
<tr>
<td>SkipNow</td>
<td>跳过：停止执行当前测试函数</td>
<td>Skip,Skipf,Skipped</td>
</tr>
<tr>
<td>Log</td>
<td>输出日志，仅在失败或者-v时候输出</td>
<td>Logf</td>
</tr>
<tr>
<td>Parallel</td>
<td>与有相同设置的测试函数并行执行（一般会设置两个以上测试用例，否则就无用了）</td>
<td></td>
</tr>
<tr>
<td>Error</td>
<td>Fail+Log</td>
<td>Errorf</td>
</tr>
<tr>
<td>Fatal</td>
<td>FailNow+Log</td>
<td>Fatalf</td>
</tr>
</tbody></table>
<p>测试命令常用的参数列表</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>说明</th>
<th>实例</th>
</tr>
</thead>
<tbody><tr>
<td>-args</td>
<td>命令行参数</td>
<td></td>
</tr>
<tr>
<td>-v</td>
<td>输出详细信息verbose</td>
<td></td>
</tr>
<tr>
<td>-parallel</td>
<td>并行执行多个测试用例</td>
<td></td>
</tr>
<tr>
<td>-run</td>
<td>运行指定测试用例，能支持正则表达式</td>
<td></td>
</tr>
<tr>
<td>-timeout</td>
<td>全部测试超时将会调用panic默认10ms</td>
<td>-timeout 1m30s</td>
</tr>
<tr>
<td>-count</td>
<td>重复测试次数</td>
<td></td>
</tr>
</tbody></table>
<h4 id="table-driven"><a href="#table-driven" class="headerlink" title="table driven"></a>table driven</h4><p>使用table将测试参数都准备好，然后推进测试。测试数据和测试逻辑分离，有利于维护。</p>
<h4 id="test-main"><a href="#test-main" class="headerlink" title="test main"></a>test main</h4><p>如果定义了TestMain函数，go test将会执行这个函数，而不是每个具体的测试用例，这样就能把测试的setup/teardown机制用起来。比如测试一些db操作，需要先构造一个db的链接之类的，就可以使用这种模式。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">TestMain</span><span class="params">(m *testing.M)</span></span> &#123;</span><br><span class="line">    code :=m.Run()</span><br><span class="line">    os.Exit(code)</span><br></pre></td></tr></table></figure>

<h4 id="example"><a href="#example" class="headerlink" title="example"></a>example</h4><p>例代码是用于给GoDoc生成帮助文档。对比stdout结果和内部Output注释是否一致来判断是否成功</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">ExampleAdd</span><span class="params">()</span></span> &#123;</span><br><span class="line">   fmt.Println(add(<span class="number">1</span>,<span class="number">2</span>))</span><br><span class="line">   fmt.Println(add(<span class="number">2</span>,<span class="number">2</span>))</span><br><span class="line">   <span class="comment">// Output:</span></span><br><span class="line">   <span class="comment">// 3</span></span><br><span class="line">   <span class="comment">// 4</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="性能测试"><a href="#性能测试" class="headerlink" title="性能测试"></a>性能测试</h3><p>文件名也是使用的 _test.go结束的。函数名称需要使用Benchmark为前缀 </p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">add</span><span class="params">(x,y <span class="keyword">int</span>)</span> <span class="title">int</span></span> &#123;</span><br><span class="line">    <span class="keyword">return</span> x+y</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">BenchmarkAdd</span><span class="params">(b *testing.B)</span></span> &#123;</span><br><span class="line">      <span class="keyword">for</span> i :=<span class="number">0</span>;i&lt;b.N;i++ &#123;</span><br><span class="line">        _ := add(<span class="number">1</span>,<span class="number">2</span>)</span><br><span class="line">     &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">go</span> test -bench</span><br></pre></td></tr></table></figure>

<p>希望忽略掉全部的单元测试，可以使用run=NONE。默认就是并发测试，可以通过-cpu参数设定多个并发限制。</p>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2020/11/18/2020/2020-11-18-go[5]-%E6%B5%8B%E8%AF%95/" data-id="ckrn3an77000z20xw8ngj9f1x" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/golang/" rel="tag">golang</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-2020/2020-11-14-golang-pprof" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/11/16/2020/2020-11-14-golang-pprof/" class="article-date">
  <time datetime="2020-11-15T16:00:00.000Z" itemprop="datePublished">2020-11-16</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/11/16/2020/2020-11-14-golang-pprof/">golang-pprof性能分析工具</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <p>编写好了golang服务之后，接着要开始关注服务的CPU，内存使用情况。golang提供了性能剖析工具，记录一些自己搜集到的信息，写下一些实践的情况。在golang中内置了pprof工具，专门来做golang语言的优化。</p>
<ul>
<li><a href="#1%E5%AE%89%E8%A3%85%E7%8E%AF%E5%A2%83">1.安装环境</a></li>
<li><a href="#2%E6%80%A7%E8%83%BD%E7%9B%91%E6%8E%A7%E4%BB%A3%E7%A0%81">2.性能监控代码</a></li>
<li><a href="#3cpu%E6%80%A7%E8%83%BD%E6%9F%A5%E7%9C%8B">3.CPU性能查看</a></li>
<li><a href="#%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F">内存泄漏</a></li>
<li><a href="#3%E5%BC%95%E7%94%A8">3.引用</a></li>
</ul>
<h1 id="1-安装环境"><a href="#1-安装环境" class="headerlink" title="1.安装环境"></a>1.安装环境</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">go get -u github.com/google/pprof</span><br></pre></td></tr></table></figure>

<p>需要去下载一个绘图的库。</p>
<p><a target="_blank" rel="noopener" href="http://www.graphviz.org/">Grahpviz</a></p>
<p>.gv文件就是dot工具能读取，并且画图的文件格式。</p>
<p>也推荐使用<a target="_blank" rel="noopener" href="https://marketplace.visualstudio.com/items?itemName=tintinweb.graphviz-interactive-preview">Graphviz Interactive-VSCode插件</a></p>
<p><a target="_blank" rel="noopener" href="https://marketplace.visualstudio.com/items?itemName=joaompinto.vscode-graphviz">vscode-graphviz语法</a></p>
<h1 id="2-性能监控代码"><a href="#2-性能监控代码" class="headerlink" title="2.性能监控代码"></a>2.性能监控代码</h1><p>这段代码将会开启一个http的网站，对外提供监控访问的地址。</p>
<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> (</span><br><span class="line">	<span class="string">&quot;net/http&quot;</span></span><br><span class="line">    _ <span class="string">&quot;net/http/pprof&quot;</span></span><br><span class="line">)</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line">    http.ListenAndServe(<span class="string">&quot;:10003&quot;</span>, <span class="literal">nil</span>)</span><br><span class="line">&#125;()</span><br></pre></td></tr></table></figure>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/rL0XUx"><img src="https://s3.ax1x.com/2020/12/30/rL0XUx.png" alt="rL0XUx.png"></a></p>
<p>能通过这个网址检查程序的内存分配，CPU占用，mutex，gorouine之类的信息。可以直接通过浏览器访问获取信息。</p>
<ul>
<li>CPU profile：报告程序的 CPU 使用情况，按照一定频率去采集应用程序在 CPU 和寄存器上面的数据</li>
<li>Memory Profile（Heap Profile）：报告程序的内存使用情况</li>
<li>Block Profiling：报告 goroutines 不在运行状态的情况，可以用来分析和查找死锁等性能瓶颈</li>
<li>Goroutine Profiling：报告 goroutines 的使用情况，有哪些 goroutine，它们的调用关系是怎样的</li>
</ul>
<h1 id="3-CPU性能查看"><a href="#3-CPU性能查看" class="headerlink" title="3.CPU性能查看"></a>3.CPU性能查看</h1><p>打开一个命令行输入：</p>
<figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">PS</span> D:\work\trunk\doc&gt; go tool pprof <span class="literal">-http</span>=<span class="string">&quot;:8081&quot;</span> http://<span class="number">127.0</span>.<span class="number">0.1</span>:<span class="number">10006</span>/debug/pprof/profile?seconds=<span class="number">200</span></span><br><span class="line">Fetching profile over HTTP from http://<span class="number">127.0</span>.<span class="number">0.1</span>:<span class="number">10006</span>/debug/pprof/profile?seconds=<span class="number">200</span></span><br><span class="line">Saved profile <span class="keyword">in</span> C:\Users\xxxxxxxxx\pprof\pprof.samples.cpu.<span class="number">003</span>.pb.gz</span><br><span class="line">Serving web UI on http://localhost:<span class="number">8081</span></span><br></pre></td></tr></table></figure>

<p>在本地将会开启 <strong>-http=”:8081”</strong> 表示在8081网站，将会呈现最后结果。这次监控将会抓取 <a target="_blank" rel="noopener" href="http://127.0.0.1:10006/debug/pprof/profile">http://127.0.0.1:10006/debug/pprof/profile</a> 提供的性能信息。**?seconds=200** 表示持续抓取200sec。到时间之后它们将会存储到本地的一个文件中： Saved profile in C:\Users\xxxxxxxxx\pprof\pprof.samples.cpu.003.pb.gz 。</p>
<p>想知道这个处理的细节，可以直接阅读golang的源码：</p>
<figure class="highlight golang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// C:\Go\src\net\http\pprof\pprof.go</span></span><br><span class="line"><span class="comment">// Profile responds with the pprof-formatted cpu profile.</span></span><br><span class="line"><span class="comment">// Profiling lasts for duration specified in seconds GET parameter, or for 30 seconds if not specified.</span></span><br><span class="line"><span class="comment">// The package initialization registers it as /debug/pprof/profile.</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Profile</span><span class="params">(w http.ResponseWriter, r *http.Request)</span></span> &#123;</span><br><span class="line">	w.Header().Set(<span class="string">&quot;X-Content-Type-Options&quot;</span>, <span class="string">&quot;nosniff&quot;</span>)</span><br><span class="line">	sec, err := strconv.ParseInt(r.FormValue(<span class="string">&quot;seconds&quot;</span>), <span class="number">10</span>, <span class="number">64</span>)</span><br><span class="line">	<span class="keyword">if</span> sec &lt;= <span class="number">0</span> || err != <span class="literal">nil</span> &#123;</span><br><span class="line">		sec = <span class="number">30</span></span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>它的接口有两个能支持输入seconds</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&quot;trace&quot;:        &quot;A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.&quot;,</span><br><span class="line">&quot;profile&quot;:      &quot;CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.&quot;,</span><br></pre></td></tr></table></figure>

<p>通过加载文件来调出分析网站：</p>
<figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">PS</span> D:\work\trunk\doc&gt; go tool pprof <span class="literal">-http</span>=<span class="string">&quot;:8989&quot;</span> C:\Users\xxxxxxxxx\pprof\pprof.samples.cpu.<span class="number">003</span>.pb.gz</span><br><span class="line">Serving web UI on http://localhost:<span class="number">8989</span></span><br></pre></td></tr></table></figure>

<p>也能通过这个命令直接加载之前监控的剖析文件。</p>
<table>
<thead>
<tr>
<th align="left">列名</th>
<th align="left">含义</th>
</tr>
</thead>
<tbody><tr>
<td align="left">flat</td>
<td align="left">函数时间（不包括子函数）。这里不包括函数等待子函数返回。</td>
</tr>
<tr>
<td align="left">flat%</td>
<td align="left">flat / 总采样时间值</td>
</tr>
<tr>
<td align="left">sum%</td>
<td align="left">前面每一行的flat占比总和</td>
</tr>
<tr>
<td align="left">cum</td>
<td align="left">函数总时间（包括子函数）。因此 flat &lt;= cum</td>
</tr>
<tr>
<td align="left">cum%</td>
<td align="left">cum占用总时间的比例</td>
</tr>
</tbody></table>
<p>火焰图方式</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/rLfQUg"><img src="https://s3.ax1x.com/2020/12/30/rLfQUg.png" alt="rLfQUg.png"></a></p>
<p>调用关系图</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/rL0OV1"><img src="https://s3.ax1x.com/2020/12/30/rL0OV1.png" alt="rL0OV1.png"></a></p>
<p>图片说明：</p>
<h1 id="内存泄漏"><a href="#内存泄漏" class="headerlink" title="内存泄漏"></a>内存泄漏</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">curl localhost:8000/debug/pprof/heap &gt; heap.base</span><br><span class="line">curl localhost:8000/debug/pprof/heap &gt; heap.current</span><br><span class="line">go tool pprof -http=:8080 -base heap.base heap.current</span><br><span class="line"></span><br><span class="line">    -diff_base <span class="built_in">source</span>     Source of base profile <span class="keyword">for</span> comparison</span><br><span class="line">    -base <span class="built_in">source</span>          Source of base profile <span class="keyword">for</span> profile subtraction</span><br></pre></td></tr></table></figure>

<h1 id="3-引用"><a href="#3-引用" class="headerlink" title="3.引用"></a>3.引用</h1><ul>
<li>[1] <a target="_blank" rel="noopener" href="https://github.com/uber-archive/go-torch">火焰图工具网站</a></li>
<li>[2] <a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/51559344">Golang性能测试工具PProf应用详解</a></li>
<li>[3] <a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/91241270">深度解密go语言之pprof</a></li>
<li>[4] <a target="_blank" rel="noopener" href="http://graphviz.org/">graphviz官网</a></li>
<li>[5] <a target="_blank" rel="noopener" href="https://www.bookstack.cn/read/eddycjy-go/tools-go-tool-pprof.md">Go 大杀器之性能剖析 PProf</a></li>
<li>[6] <a target="_blank" rel="noopener" href="https://sourceforge.net/projects/qcachegrindwin/">qcachegrindwin下载地址</a></li>
<li>[7] <a target="_blank" rel="noopener" href="https://studygolang.com/articles/29812?fr=sidebar">golang内存泄漏的排查记录</a></li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2020/11/16/2020/2020-11-14-golang-pprof/" data-id="ckrn3an72000p20xw5tqq2xdt" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/golang/" rel="tag">golang</a></li></ul>

    </footer>
  </div>
  
</article>


  
    <article id="post-2020/2020-11-16-supervisor" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/11/16/2020/2020-11-16-supervisor/" class="article-date">
  <time datetime="2020-11-15T16:00:00.000Z" itemprop="datePublished">2020-11-16</time>
</a>
    
  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 itemprop="name">
      <a class="article-title" href="/2020/11/16/2020/2020-11-16-supervisor/">Supervisor使用详解</a>
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>supervisor是用Python开发的一个client/server服务，是Linux/Unix系统下的一个进程管理工具。可以很方便的监听、启动、停止、重启一个或多个进程。用supervisor管理的进程，当一个进程意外被杀死，supervisor监听到进程死后，会自动将它重启，很方便的做到进程自动恢复的功能，不再需要自己写shell脚本来控制。</p>
<h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># ubuntu安装：</span></span><br><span class="line">sudo apt-get install supervisor</span><br><span class="line"><span class="comment"># centos安装</span></span><br><span class="line">yum install -y supervisor</span><br></pre></td></tr></table></figure>

<p>检查是否一个守护进程</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">[root@test-qingzhou-01 supervisord.d]<span class="comment"># ps aux |grep supervisord</span></span><br><span class="line">root      4154  0.0  0.1  56144 15060 pts/0    Ss+  13:31   0:00 /usr/bin/python /usr/bin/supervisord -nc /etc/supervisor/supervisord.conf</span><br><span class="line"><span class="comment"># 手动启动方式</span></span><br><span class="line">supervisord -c /etc/supervisor/supervisord.conf</span><br><span class="line"><span class="comment"># 手动关闭</span></span><br><span class="line">ps aux |grep -v grep | grep supervisord | awk <span class="string">&#x27;&#123;print $2&#125;&#x27;</span> |xargs <span class="built_in">kill</span> -2</span><br></pre></td></tr></table></figure>

<h1 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h1><p>系统配置位置：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">/etc/supervisor/supervisord.conf</span><br><span class="line"></span><br><span class="line">开启一个http的服务，方便外部访问：</span><br><span class="line">[inet_http_server]         ; inet (TCP) server disabled by default</span><br><span class="line"> port=0.0.0.0:10080        ; (ip_address:port specifier, *:port <span class="keyword">for</span> all iface)                                                  </span><br><span class="line"> username=user              ; (default is no username (open server))</span><br><span class="line"> password=123               ; (default is no password (open server))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 修改默认进程数字：</span></span><br><span class="line">minfds=65535                ; (min. avail startup file descriptors;default 1024)</span><br><span class="line">minprocs=65535              ; (min. avail process descriptors;default 200)</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>配置自己的ini</p>
<figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="section">[group:qzsrv3]</span></span><br><span class="line"><span class="attr">programs</span>=Lobby,Gateway,Scene,Matcher,Level,DBServer</span><br><span class="line"></span><br><span class="line"><span class="section">[program:Login]</span></span><br><span class="line"><span class="attr">directory</span>=/home/teamcityagent/BuildAgent/work/<span class="number">5</span>c951883da1eba28/server/bin</span><br><span class="line"><span class="attr">command</span>=/home/teamcityagent/BuildAgent/work/<span class="number">5</span>c951883da1eba28/server/bin/Login srv3</span><br><span class="line"><span class="attr">autostart</span>=<span class="literal">false</span></span><br><span class="line"><span class="attr">autorestart</span>=unexpected</span><br><span class="line"><span class="attr">startsecs</span>=<span class="number">1</span></span><br><span class="line"><span class="attr">stderr_logfile</span>=/home/teamcityagent/BuildAgent/work/<span class="number">5</span>c951883da1eba28/server/log/Login.err.log</span><br><span class="line"><span class="attr">stdout_logfile</span>=/home/teamcityagent/BuildAgent/work/<span class="number">5</span>c951883da1eba28/server/log/Login.log</span><br><span class="line"><span class="attr">user</span>=teamcityagent</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>这里可以指定是否出现问题之后直接重启。</p>
<h1 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h1><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">[root@test-qingzhou-01 supervisor]<span class="comment"># /usr/local/bin/supervisord -c ./supervisrd.conf </span></span><br><span class="line">Error: Another program is already listening on a port that one of our HTTP servers is configured to use.  Shut this program down first before starting supervisord.</span><br><span class="line"></span><br><span class="line">[root@test-qingzhou-01 supervisor]<span class="comment"># find / -name supervisor.sock</span></span><br><span class="line">/run/supervisor/supervisor.sock</span><br><span class="line">/var/lib/docker/overlay2/d6de9ac36e7b3c66fed11ddb3cf384a18932d3f8044dda5bb2247476604acd71/diff/run/supervisor.sock</span><br><span class="line">/var/lib/docker/overlay2/d6de9ac36e7b3c66fed11ddb3cf384a18932d3f8044dda5bb2247476604acd71/merged/run/supervisor.sock</span><br><span class="line">[root@test-qingzhou-01 supervisor]<span class="comment"># unlink /run/supervisor/supervisor.sock</span></span><br><span class="line">[root@test-qingzhou-01 supervisor]<span class="comment"># unlink /var/lib/docker/overlay2/d6de9ac36e7b3c66fed11ddb3cf384a18932d3f8044dda5bb2247476604acd71/diff/run/supervisor.sock</span></span><br><span class="line">[root@test-qingzhou-01 supervisor]<span class="comment"># unlink /var/lib/docker/overlay2/d6de9ac36e7b3c66fed11ddb3cf384a18932d3f8044dda5bb2247476604acd71/merged/run/supervisor.sock</span></span><br><span class="line">unlink: cannot unlink ‘/var/lib/docker/overlay2/d6de9ac36e7b3c66fed11ddb3cf384a18932d3f8044dda5bb2247476604acd71/merged/run/supervisor.sock’: No such file or directory</span><br></pre></td></tr></table></figure>

<h1 id="web"><a href="#web" class="headerlink" title="web"></a>web</h1><p>在后台能直接操作服务器启动</p>
<p><a target="_blank" rel="noopener" href="https://imgchr.com/i/DAtsTe"><img src="https://s3.ax1x.com/2020/11/16/DAtsTe.png" alt="DAtsTe.png"></a></p>
<h1 id="XML-RPC-API"><a href="#XML-RPC-API" class="headerlink" title="XML-RPC API"></a>XML-RPC API</h1><p>可以通过编写python脚本来对web做操作。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> xmlrpc.client <span class="keyword">import</span> ServerProxy</span><br><span class="line"></span><br><span class="line"><span class="comment"># &#x27;http://username:password@localhost:9001/RPC2&#x27;</span></span><br><span class="line">server = ServerProxy(<span class="string">&#x27;http://user:123@172.16.11.92:10080/RPC2&#x27;</span>,verbose=<span class="literal">True</span>)</span><br><span class="line"><span class="comment"># 输出全部的API函数，以xml文件方式输出，可以直接查阅函数，调用就好了。</span></span><br><span class="line"><span class="comment">#print(server.system.listMethods())</span></span><br><span class="line"><span class="comment"># 查看服务器状态</span></span><br><span class="line"><span class="built_in">print</span>(server.supervisor.getState())</span><br><span class="line"><span class="comment"># 关闭全部的服务器</span></span><br><span class="line"><span class="comment">#print(server.supervisor.stopAllProcesses())</span></span><br><span class="line"><span class="comment"># 打开全部的服务器</span></span><br><span class="line"><span class="built_in">print</span>(server.supervisor.startAllProcesses())</span><br></pre></td></tr></table></figure>

<h1 id="supervisord-monitor"><a href="#supervisord-monitor" class="headerlink" title="supervisord-monitor"></a>supervisord-monitor</h1><p><a target="_blank" rel="noopener" href="https://github.com/mlazarov/supervisord-monitor">参考文档-monitor</a></p>
<h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><ul>
<li>[1] <a target="_blank" rel="noopener" href="https://www.jianshu.com/p/0b9054b33db3">Supervisor 使用详解</a></li>
<li>[2] <a target="_blank" rel="noopener" href="http://supervisord.org/">Supervisor官网</a></li>
<li>[3] <a target="_blank" rel="noopener" href="https://www.cnblogs.com/zhoujinyi/p/6073705.html">进程管理supervisor的简单说明</a></li>
<li>[4] <a target="_blank" rel="noopener" href="https://blog.csdn.net/zou79189747/article/details/80403016">supervisor 使用详解</a></li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="http://example.com/2020/11/16/2020/2020-11-16-supervisor/" data-id="ckrn3an73000r20xw81sf34jx" class="article-share-link">Share</a>
      
      
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/Linux/" rel="tag">Linux</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E8%BF%90%E7%BB%B4/" rel="tag">运维</a></li></ul>

    </footer>
  </div>
  
</article>


  


  <nav id="page-nav">
    
    <a class="extend prev" rel="prev" href="/page/2/">&amp;laquo; Prev</a><a class="page-number" href="/">1</a><a class="page-number" href="/page/2/">2</a><span class="page-number current">3</span><a class="page-number" href="/page/4/">4</a><a class="page-number" href="/page/5/">5</a><a class="extend next" rel="next" href="/page/4/">Next &amp;raquo;</a>
  </nav>

</section>
        
          <aside id="sidebar">
  
    

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tags</h3>
    <div class="widget">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/tags/3D/" rel="tag">3D</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/AI/" rel="tag">AI</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/C/" rel="tag">C++</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Game/" rel="tag">Game</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Go/" rel="tag">Go</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Linux/" rel="tag">Linux</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Math/" rel="tag">Math</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Server/" rel="tag">Server</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Unreal-Engine4/" rel="tag">Unreal Engine4</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/" rel="tag">git</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/golang/" rel="tag">golang</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/guitar/" rel="tag">guitar</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/node-js/" rel="tag">node.js</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/protobuf/" rel="tag">protobuf</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/python/" rel="tag">python</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/recast/" rel="tag">recast</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/redis/" rel="tag">redis</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/type/" rel="tag">type</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vscode/" rel="tag">vscode</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/%E8%BF%90%E7%BB%B4/" rel="tag">运维</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tag Cloud</h3>
    <div class="widget tagcloud">
      <a href="/tags/3D/" style="font-size: 10px;">3D</a> <a href="/tags/AI/" style="font-size: 11.67px;">AI</a> <a href="/tags/C/" style="font-size: 15px;">C++</a> <a href="/tags/Game/" style="font-size: 18.33px;">Game</a> <a href="/tags/Go/" style="font-size: 13.33px;">Go</a> <a href="/tags/Linux/" style="font-size: 13.33px;">Linux</a> <a href="/tags/Math/" style="font-size: 11.67px;">Math</a> <a href="/tags/Server/" style="font-size: 16.67px;">Server</a> <a href="/tags/Unreal-Engine4/" style="font-size: 10px;">Unreal Engine4</a> <a href="/tags/git/" style="font-size: 10px;">git</a> <a href="/tags/golang/" style="font-size: 20px;">golang</a> <a href="/tags/guitar/" style="font-size: 10px;">guitar</a> <a href="/tags/node-js/" style="font-size: 10px;">node.js</a> <a href="/tags/protobuf/" style="font-size: 10px;">protobuf</a> <a href="/tags/python/" style="font-size: 10px;">python</a> <a href="/tags/recast/" style="font-size: 11.67px;">recast</a> <a href="/tags/redis/" style="font-size: 10px;">redis</a> <a href="/tags/type/" style="font-size: 10px;">type</a> <a href="/tags/vscode/" style="font-size: 10px;">vscode</a> <a href="/tags/%E8%BF%90%E7%BB%B4/" style="font-size: 16.67px;">运维</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Archives</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/07/">July 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/06/">June 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/04/">April 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/03/">March 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/01/">January 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/12/">December 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/11/">November 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/03/">March 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2018/11/">November 2018</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Recent Posts</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2021/07/19/2021/2021-07-19-etcd-learn/">go-etcd阅读笔记</a>
          </li>
        
          <li>
            <a href="/2021/07/19/2021/2021-07-19-socketop-learn/">REUSEPORT-REUSEADDR-学习</a>
          </li>
        
          <li>
            <a href="/2021/06/22/2021/2021-06-22-go-ethereum-learn/">go-ethereum阅读笔记</a>
          </li>
        
          <li>
            <a href="/2021/06/21/2021/2021-06-21-server%E6%B5%8B%E8%AF%95%E5%B7%A5%E5%85%B7/">server测试工具</a>
          </li>
        
          <li>
            <a href="/2021/06/09/2021/2021-06-09-pprof-doc/">go-pprof</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      &copy; 2021 Abel Sean<br>
      Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>
    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    

<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>


  
<link rel="stylesheet" href="/fancybox/jquery.fancybox.css">

  
<script src="/fancybox/jquery.fancybox.pack.js"></script>




<script src="/js/script.js"></script>




  </div>
</body>
</html>